import keyboard
import mouse

from tools.virtual_key_code import get_key_state
from tools.virtual_key_code import vk
from bdtime import tt
from tools.utils import Key
from tools.utils import Mouse
from copy import deepcopy
from bdtime import Time
import os
import json
from tools.remove_temp_file import remove_path
from tools.beep import beep
from tools.recoder_args import DefaultArgs


ms = Mouse()
kk = Key()


class MouseStateRecorder:
    def __init__(self, frames=DefaultArgs.frames, tt=None, save_init_position=True):
        self.frames = frames
        self.save_dir = os.path.join(*['..', 'tempdir', 'recorded_states', 'rel_mouse'])

        self.key__click_left = DefaultArgs.key__click_mouse_left
        self.key__shift_speed = DefaultArgs.key__shift_mouse_speed

        self.targets = ['left', 'right', 'up', 'down', self.key__click_left, self.key__shift_speed]
        self.recorded_states = []
        self.recorded_states.insert(0, {'recorder_type': self.__class__.__name__})

        self.states = dict(zip(self.targets, [0] * len(self.targets)))
        self.is_recording = False

        self.tt = tt

        self.save_init_position = save_init_position

        self.init_states()

    def get_save_file_path(self, file_name):
        file_path = os.path.join(self.save_dir, file_name)
        return file_path

    def init_states(self):
        self.states = dict(zip(self.targets, [0] * len(self.targets)))

    def record(self, until='alt + x'):
        tt = self.tt if self.tt else Time()

        self.is_recording = True

        if self.save_init_position:
            self.recorded_states.append({'mouse_position': ms.position, 'time': tt.now()})

        i = 0
        while True:
            if not self.is_recording or tt.stop(until, raise_error=False):
                print(f'*** stop {self.__class__.__name__}.record by user!')
                break

            if DefaultArgs.flag__exit:
                break

            next_record_time = DefaultArgs.record_time_schedule[i]
            while tt.now() < next_record_time:
                if DefaultArgs.use_sleep_wait:
                    tt.sleep(0.001)

            self.states.update({'i': i})
            self.states.update({'time': next_record_time})
            # self.states.update({'time': tt.now()})

            for key in self.targets:
                state = get_key_state(key)
                if state:
                    self.states[key] = 1
                else:
                    self.states[key] = 0

            self.recorded_states.append(deepcopy(self.states))

            i += 1

        self.is_recording = False
        return self.recorded_states

    def play(self, recorded_states=None, speed=1):
        if not recorded_states:
            recorded_states = self.recorded_states

        self.init_states()

        # if speed != 1:
        #     assert speed, 'speed不能为0!'
        #     for state in recorded_states:
        #         state['time'] = state['time'] / speed

        tt = Time()
        for state in recorded_states:
            if tt.stop('alt + x', raise_error=False):
                print(f'*** stop {self.__class__.__name__}.play by user!')
                break

            time = state.get('time')
            if time is None:
                continue

            while tt.now() * speed < time:
                tt.sleep(0.001)
                continue

            for k, v in state.items():
                if k == 'mouse_position':
                    ms.position = v
                    continue

                if k == 'time':
                    continue

                if k in events_dc:
                    if v:
                        event_i = events_dc[k]
                        event_i()
                else:
                    # print('--- not in events_dc:', k, v)
                    if k == self.key__click_left:
                        if v:
                            mouse_events.press_left()
                        else:
                            mouse_events.release_left()
                    elif k == self.key__shift_speed:
                        if v:
                            mouse_events.shift_speed()

    def press(self, key):
        if self.states[key]:
            return

        kk.press(key)
        self.states[key] = 1

    def release(self, key):
        if not self.states[key]:
            return

        kk.release(key)
        self.states[key] = 0

    def stop_record(self):
        self.is_recording = False

    def save_record(self, file_name: str = ""):
        os.makedirs(self.save_dir, exist_ok=True)

        data = json.dumps(self.recorded_states, indent=4)

        timestamp = tt.get_current_beijing_time_str(tt.common_date_time_formats.ms_int[-8:])

        file_name = file_name if file_name else f'record__{timestamp}.json'

        file_path = os.path.join(self.save_dir, file_name)
        with open(file_path, 'w+') as f:
            f.write(data)

        print(f'{self.__class__.__name__} --- save_record to --- {file_path}')
        return file_path

    def load_record(self, file_name):
        file_path = os.path.join(self.save_dir, file_name)
        with open(file_path, 'r') as f:
            self.recorded_states = json.load(f)
        e = self.recorded_states[0]
        assert e['recorder_type'] == self.__class__.__name__

    def remove_all_recorded_states(self):
        remove_path(self.save_dir, keep_external_folder=True)


class MouseEvents:
    def __init__(self, frames=DefaultArgs.frames):
        self.frames = frames
        self.dx = self.dy = 10 * DefaultArgs.mouse_move_speed
        self.is_pressed_left = False

        self.speed_index = 1
        self.speed_ls = [0.1, 1, 3]
        self.last_shit_speed_time = Time()

        self.speed = self.speed_ls[self.speed_index]

    # @property
    # def speed(self):
    #     return self.speed_ls[self.speed_index]

    def change_mouse_speed_to_quick(self):

        if self.last_shit_speed_time.now() < 0.5:
            return
        self.last_shit_speed_time.__init__()

        self.speed = self.speed_ls[2]

    def change_mouse_speed_to_normal(self):

        if self.last_shit_speed_time.now() < 0.5:
            return
        self.last_shit_speed_time.__init__()

        self.speed = self.speed_ls[1]

    def change_mouse_speed_to_slow(self):

        if self.last_shit_speed_time.now() < 0.5:
            return
        self.last_shit_speed_time.__init__()

        self.speed = self.speed_ls[0]

    def shift_speed(self):
        if self.last_shit_speed_time.now() < 0.5:
            return
        self.last_shit_speed_time.__init__()

        beep()
        self.speed_index += 1
        self.speed_index %= len(self.speed_ls)
        self.speed = self.speed_ls[self.speed_index]
        print('~~~ self.speed', self.speed, f'--- frames: {self.frames} --- dx, dy:', self.dx, self.dy)

    def press_left(self):
        if self.is_pressed_left:
            return

        # if ms.is_pressed(ms.buttons.left):
        #     return

        # print('~~~ press_left', tt.now())

        ms.press(ms.buttons.left)
        self.is_pressed_left = True

    def release_left(self):
        if not self.is_pressed_left:
            return

        # if not ms.is_pressed(ms.buttons.left):
        #     return

        # print('~~~ release_left', tt.now())

        ms.release(ms.buttons.left)
        self.is_pressed_left = False

    def move_left(self):
        dx = int(-self.dx * self.speed)
        ms.move_r(dx, 0)

    def move_right(self):
        dx = int(self.dx * self.speed)
        ms.move_r(dx, 0)

    def move_up(self):
        dy = int(-self.dy * self.speed)
        ms.move_r(0, dy)

    def move_down(self):
        dy = int(self.dy * self.speed)
        ms.move_r(0, dy)


mouse_events = MouseEvents()


events_dc = {
    'left': mouse_events.move_left,
    'right': mouse_events.move_right,
    'up': mouse_events.move_up,
    'down': mouse_events.move_down,
}


def run(save_file_name: (None, str) = "", play=False, remove_else_recoded=False, mouse_state_recorder=None, tt=None, play_target=False, end_call=None):
    if not mouse_state_recorder:
        tt = tt if tt else Time()
        mouse_state_recorder = MouseStateRecorder(tt=tt)
    else:
        tt = mouse_state_recorder.tt

    try:
        if play_target is True:
            play_target = save_file_name

        if play_target:
            mouse_state_recorder.load_record(play_target)
        else:
            mouse_state_recorder.record()

            if remove_else_recoded:
                mouse_state_recorder.remove_all_recorded_states()

            if save_file_name is not None:
                mouse_state_recorder.save_record(file_name=save_file_name)

        if play:
            tt.sleep(1)
            print(f'====== start mouse_state_recorder.play, tt.now: {tt.now()}')
            beep(400)
            mouse_state_recorder.play(speed=1)

        if end_call:
            end_call()

    except Exception as e:
        beep(400, 0.3)
        beep(600, 0.3)
        beep(800, 0.3)
        raise e


def main():
    save_file_name = 'test'
    play = False

    hotkey = 'alt + 1'
    print(f'--- wait hotkey[{hotkey}]!')

    keyboard.wait(hotkey)

    tt.sleep(0.5)
    print('--- start main__mouse_recorder!')
    beep()

    run(save_file_name=save_file_name, play=play)


if __name__ == '__main__':
    main()
